/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	mygame "MyGame"          // refers to generated code
	example "MyGame/Example" // refers to generated code
	"encoding/json"
	optional_scalars "optional_scalars" // refers to generated code

	"bytes"
	"flag"
	"fmt"
	"os"
	"reflect"
	"sort"
	"testing"
	"testing/quick"

	flatbuffers "github.com/google/flatbuffers/go"
)

var (
	cppData, javaData, outData string
	fuzz                       bool
	fuzzFields, fuzzObjects    int
)

func init() {
	flag.StringVar(&cppData, "cpp_data", "",
		"location of monsterdata_test.mon to verify against (required)")
	flag.StringVar(&javaData, "java_data", "",
		"location of monsterdata_java_wire.mon to verify against (optional)")
	flag.StringVar(&outData, "out_data", "",
		"location to write generated Go data")
	flag.BoolVar(&fuzz, "fuzz", false, "perform fuzzing")
	flag.IntVar(&fuzzFields, "fuzz_fields", 4, "fields per fuzzer object")
	flag.IntVar(&fuzzObjects, "fuzz_objects", 10000,
		"number of fuzzer objects (higher is slower and more thorough")
}

// Store specific byte patterns in these variables for the fuzzer. These
// values are taken verbatim from the C++ function FuzzTest1.
var (
	overflowingInt32Val = flatbuffers.GetInt32([]byte{0x83, 0x33, 0x33, 0x33})
	overflowingInt64Val = flatbuffers.GetInt64([]byte{0x84, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44})
)

func TestMain(m *testing.M) {
	flag.Parse()
	if cppData == "" {
		fmt.Fprintf(os.Stderr, "cpp_data argument is required\n")
		os.Exit(1)
	}
	os.Exit(m.Run())
}

// TestTextParsing test if text parsing works with object API.
func TestTextParsing(t *testing.T) {
	expectedMonster := example.MonsterT{
		Mana:                  42,
		Name:                  "foo",
		LongEnumNormalDefault: example.LongEnumLongTwo,
	}

	buf := new(bytes.Buffer)
	if err := json.NewEncoder(buf).Encode(expectedMonster); err != nil {
		t.Fatal(err)
	}

	var monster example.MonsterT
	if err := json.NewDecoder(buf).Decode(&monster); err != nil {
		t.Fatal(err)
	}

	if monster.Mana != expectedMonster.Mana {
		t.Fatal("wrong mana:", monster.Mana)
	}
	if monster.Name != expectedMonster.Name {
		t.Fatal("wrong name:", monster.Name)
	}
	if monster.LongEnumNormalDefault != expectedMonster.LongEnumNormalDefault {
		t.Fatal("wrong enum:", monster.LongEnumNormalDefault)
	}
}

// TestAll runs all checks, failing if any errors occur.
func TestAll(t *testing.T) {
	// Verify that the Go FlatBuffers runtime library generates the
	// expected bytes (does not use any schema):
	CheckByteLayout(t.Fatalf)
	CheckMutateMethods(t.Fatalf)

	// Verify that panics are raised during exceptional conditions:
	CheckNotInObjectError(t.Fatalf)
	CheckStringIsNestedError(t.Fatalf)
	CheckByteStringIsNestedError(t.Fatalf)
	CheckStructIsNotInlineError(t.Fatalf)
	CheckFinishedBytesError(t.Fatalf)
	CheckSharedStrings(t.Fatalf)
	CheckEmptiedBuilder(t.Fatalf)

	// Verify that GetRootAs works for non-root tables
	CheckGetRootAsForNonRootTable(t.Fatalf)
	CheckTableAccessors(t.Fatalf)

	// Verify that using the generated Go code builds a buffer without
	// returning errors:
	generated, off := CheckGeneratedBuild(false, t.Fatalf)

	// Verify that the buffer generated by Go code is readable by the
	// generated Go code:
	CheckReadBuffer(generated, off, false, t.Fatalf)
	CheckMutateBuffer(generated, off, false, t.Fatalf)
	CheckObjectAPI(generated, off, false, t.Fatalf)

	// Verify that the buffer generated by C++ code is readable by the
	// generated Go code:
	monsterDataCpp, err := os.ReadFile(cppData)
	if err != nil {
		t.Fatal(err)
	}
	CheckReadBuffer(monsterDataCpp, 0, false, t.Fatalf)
	CheckMutateBuffer(monsterDataCpp, 0, false, t.Fatalf)
	CheckObjectAPI(monsterDataCpp, 0, false, t.Fatalf)

	// Verify that vtables are deduplicated when written:
	CheckVtableDeduplication(t.Fatalf)

	// Verify the enum names
	CheckEnumNames(t.Fatalf)

	// Verify enum String methods
	CheckEnumString(t.Fatalf)

	// Verify the enum values maps
	CheckEnumValues(t.Fatalf)

	// Verify that the Go code used in FlatBuffers documentation passes
	// some sanity checks:
	CheckDocExample(generated, off, t.Fatalf)

	// Check Builder.CreateByteVector
	CheckCreateByteVector(t.Fatalf)

	// Check a parent namespace import
	CheckParentNamespace(t.Fatalf)

	// Check size-prefixed flatbuffers
	CheckSizePrefixedBuffer(t.Fatalf)

	// Check that optional scalars work
	CheckOptionalScalars(t.Fatalf)

	// If the filename of the FlatBuffers file generated by the Java test
	// is given, check that Go code can read it, and that Go code
	// generates an identical buffer when used to create the example data:
	if javaData != "" {
		monsterDataJava, err := os.ReadFile(javaData)
		if err != nil {
			t.Fatal(err)
		}
		CheckReadBuffer(monsterDataJava, 0, false, t.Fatalf)
		CheckByteEquality(generated[off:], monsterDataJava, t.Fatalf)
	}

	// Verify that various fuzzing scenarios produce a valid FlatBuffer.
	if fuzz {
		checkFuzz(fuzzFields, fuzzObjects, t.Fatalf)
	}

	// Write the generated buffer out to a file:
	err = os.WriteFile(outData, generated[off:], os.FileMode(0644))
	if err != nil {
		t.Fatal(err)
	}
}

// CheckReadBuffer checks that the given buffer is evaluated correctly
// as the example Monster.
func CheckReadBuffer(buf []byte, offset flatbuffers.UOffsetT, sizePrefix bool, fail func(string, ...interface{})) {
	// try the two ways of generating a monster
	var monster1 *example.Monster
	monster2 := &example.Monster{}

	if sizePrefix {
		monster1 = example.GetSizePrefixedRootAsMonster(buf, offset)
		flatbuffers.GetSizePrefixedRootAs(buf, offset, monster2)
	} else {
		monster1 = example.GetRootAsMonster(buf, offset)
		flatbuffers.GetRootAs(buf, offset, monster2)
	}

	for _, monster := range []*example.Monster{monster1, monster2} {
		if got := monster.Hp(); 80 != got {
			fail(FailString("hp", 80, got))
		}

		// default
		if got := monster.Mana(); 150 != got {
			fail(FailString("mana", 150, got))
		}

		if got := monster.Name(); !bytes.Equal([]byte("MyMonster"), got) {
			fail(FailString("name", "MyMonster", got))
		}

		if got := monster.Color(); example.ColorBlue != got {
			fail(FailString("color", example.ColorBlue, got))
		}

		if got := monster.Testbool(); true != got {
			fail(FailString("testbool", true, got))
		}

		// initialize a Vec3 from Pos()
		vec := new(example.Vec3)
		vec = monster.Pos(vec)
		if vec == nil {
			fail("vec3 initialization failed")
		}

		// check that new allocs equal given ones:
		vec2 := monster.Pos(nil)
		if !reflect.DeepEqual(vec, vec2) {
			fail("fresh allocation failed")
		}

		// verify the properties of the Vec3
		if got := vec.X(); float32(1.0) != got {
			fail(FailString("Pos.X", float32(1.0), got))
		}

		if got := vec.Y(); float32(2.0) != got {
			fail(FailString("Pos.Y", float32(2.0), got))
		}

		if got := vec.Z(); float32(3.0) != got {
			fail(FailString("Pos.Z", float32(3.0), got))
		}

		if got := vec.Test1(); float64(3.0) != got {
			fail(FailString("Pos.Test1", float64(3.0), got))
		}

		if got := vec.Test2(); example.ColorGreen != got {
			fail(FailString("Pos.Test2", example.ColorGreen, got))
		}

		// initialize a Test from Test3(...)
		t := new(example.Test)
		t = vec.Test3(t)
		if t == nil {
			fail("vec.Test3(&t) failed")
		}

		// check that new allocs equal given ones:
		t2 := vec.Test3(nil)
		if !reflect.DeepEqual(t, t2) {
			fail("fresh allocation failed")
		}

		// verify the properties of the Test
		if got := t.A(); int16(5) != got {
			fail(FailString("t.A()", int16(5), got))
		}

		if got := t.B(); int8(6) != got {
			fail(FailString("t.B()", int8(6), got))
		}

		if got := monster.TestType(); example.AnyMonster != got {
			fail(FailString("monster.TestType()", example.AnyMonster, got))
		}

		// initialize a Table from a union field Test(...)
		var table2 flatbuffers.Table
		if ok := monster.Test(&table2); !ok {
			fail("monster.Test(&monster2) failed")
		}

		// initialize a Monster from the Table from the union
		var monster2 example.Monster
		monster2.Init(table2.Bytes, table2.Pos)

		if got := monster2.Name(); !bytes.Equal([]byte("Fred"), got) {
			fail(FailString("monster2.Name()", "Fred", got))
		}

		inventorySlice := monster.InventoryBytes()
		if len(inventorySlice) != monster.InventoryLength() {
			fail(FailString("len(monster.InventoryBytes) != monster.InventoryLength", len(inventorySlice), monster.InventoryLength()))
		}

		if got := monster.InventoryLength(); 5 != got {
			fail(FailString("monster.InventoryLength", 5, got))
		}

		invsum := 0
		l := monster.InventoryLength()
		for i := 0; i < l; i++ {
			v := monster.Inventory(i)
			if v != inventorySlice[i] {
				fail(FailString("monster inventory slice[i] != Inventory(i)", v, inventorySlice[i]))
			}
			invsum += int(v)
		}
		if invsum != 10 {
			fail(FailString("monster inventory sum", 10, invsum))
		}

		if got := monster.Test4Length(); 2 != got {
			fail(FailString("monster.Test4Length()", 2, got))
		}

		var test0 example.Test
		ok := monster.Test4(&test0, 0)
		if !ok {
			fail(FailString("monster.Test4(&test0, 0)", true, ok))
		}

		var test1 example.Test
		ok = monster.Test4(&test1, 1)
		if !ok {
			fail(FailString("monster.Test4(&test1, 1)", true, ok))
		}

		// the position of test0 and test1 are swapped in monsterdata_java_wire
		// and monsterdata_test_wire, so ignore ordering
		v0 := test0.A()
		v1 := test0.B()
		v2 := test1.A()
		v3 := test1.B()
		sum := int(v0) + int(v1) + int(v2) + int(v3)

		if 100 != sum {
			fail(FailString("test0 and test1 sum", 100, sum))
		}

		if got := monster.TestarrayofstringLength(); 2 != got {
			fail(FailString("Testarrayofstring length", 2, got))
		}

		if got := monster.Testarrayofstring(0); !bytes.Equal([]byte("test1"), got) {
			fail(FailString("Testarrayofstring(0)", "test1", got))
		}

		if got := monster.Testarrayofstring(1); !bytes.Equal([]byte("test2"), got) {
			fail(FailString("Testarrayofstring(1)", "test2", got))
		}
	}
}

// CheckMutateBuffer checks that the given buffer can be mutated correctly
// as the example Monster. Only available scalar values are mutated.
func CheckMutateBuffer(org []byte, offset flatbuffers.UOffsetT, sizePrefix bool, fail func(string, ...interface{})) {
	// make a copy to mutate
	buf := make([]byte, len(org))
	copy(buf, org)

	// load monster data from the buffer
	var monster *example.Monster
	if sizePrefix {
		monster = example.GetSizePrefixedRootAsMonster(buf, offset)
	} else {
		monster = example.GetRootAsMonster(buf, offset)
	}

	// test case struct
	type testcase struct {
		field  string
		testfn func() bool
	}

	testForOriginalValues := []testcase{
		testcase{"Hp", func() bool { return monster.Hp() == 80 }},
		testcase{"Mana", func() bool { return monster.Mana() == 150 }},
		testcase{"Testbool", func() bool { return monster.Testbool() == true }},
		testcase{"Pos.X'", func() bool { return monster.Pos(nil).X() == float32(1.0) }},
		testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(2.0) }},
		testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(3.0) }},
		testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(3.0) }},
		testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == example.ColorGreen }},
		testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(5) }},
		testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(6) }},
		testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(2) }},
	}

	testMutability := []testcase{
		testcase{"Hp", func() bool { return monster.MutateHp(70) }},
		testcase{"Mana", func() bool { return !monster.MutateMana(140) }},
		testcase{"Testbool", func() bool { return monster.MutateTestbool(false) }},
		testcase{"Pos.X", func() bool { return monster.Pos(nil).MutateX(10.0) }},
		testcase{"Pos.Y", func() bool { return monster.Pos(nil).MutateY(20.0) }},
		testcase{"Pos.Z", func() bool { return monster.Pos(nil).MutateZ(30.0) }},
		testcase{"Pos.Test1", func() bool { return monster.Pos(nil).MutateTest1(30.0) }},
		testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(example.ColorBlue) }},
		testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).MutateA(50) }},
		testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).MutateB(60) }},
		testcase{"Inventory[2]", func() bool { return monster.MutateInventory(2, 200) }},
	}

	testForMutatedValues := []testcase{
		testcase{"Hp", func() bool { return monster.Hp() == 70 }},
		testcase{"Mana", func() bool { return monster.Mana() == 150 }},
		testcase{"Testbool", func() bool { return monster.Testbool() == false }},
		testcase{"Pos.X'", func() bool { return monster.Pos(nil).X() == float32(10.0) }},
		testcase{"Pos.Y'", func() bool { return monster.Pos(nil).Y() == float32(20.0) }},
		testcase{"Pos.Z'", func() bool { return monster.Pos(nil).Z() == float32(30.0) }},
		testcase{"Pos.Test1'", func() bool { return monster.Pos(nil).Test1() == float64(30.0) }},
		testcase{"Pos.Test2'", func() bool { return monster.Pos(nil).Test2() == example.ColorBlue }},
		testcase{"Pos.Test3.A", func() bool { return monster.Pos(nil).Test3(nil).A() == int16(50) }},
		testcase{"Pos.Test3.B", func() bool { return monster.Pos(nil).Test3(nil).B() == int8(60) }},
		testcase{"Inventory[2]", func() bool { return monster.Inventory(2) == byte(200) }},
	}

	testInvalidEnumValues := []testcase{
		testcase{"Pos.Test2", func() bool { return monster.Pos(nil).MutateTest2(example.Color(20)) }},
		testcase{"Pos.Test2", func() bool { return monster.Pos(nil).Test2() == example.Color(20) }},
	}

	// make sure original values are okay
	for _, t := range testForOriginalValues {
		if !t.testfn() {
			fail("field '" + t.field + "' doesn't have the expected original value")
		}
	}

	// try to mutate fields and check mutability
	for _, t := range testMutability {
		if !t.testfn() {
			fail(FailString("field '"+t.field+"' failed mutability test", true, false))
		}
	}

	// test whether values have changed
	for _, t := range testForMutatedValues {
		if !t.testfn() {
			fail("field '" + t.field + "' doesn't have the expected mutated value")
		}
	}

	// make sure the buffer has changed
	if reflect.DeepEqual(buf, org) {
		fail("mutate buffer failed")
	}

	// To make sure the buffer has changed accordingly
	// Read data from the buffer and verify all fields
	if sizePrefix {
		monster = example.GetSizePrefixedRootAsMonster(buf, offset)
	} else {
		monster = example.GetRootAsMonster(buf, offset)
	}

	for _, t := range testForMutatedValues {
		if !t.testfn() {
			fail("field '" + t.field + "' doesn't have the expected mutated value")
		}
	}

	// a couple extra tests for "invalid" enum values, which don't correspond to
	// anything in the schema, but are allowed
	for _, t := range testInvalidEnumValues {
		if !t.testfn() {
			fail("field '" + t.field + "' doesn't work with an invalid enum value")
		}
	}

	// reverting all fields to original values should
	// re-create the original buffer. Mutate all fields
	// back to their original values and compare buffers.
	// This test is done to make sure mutations do not do
	// any unnecessary changes to the buffer.
	if sizePrefix {
		monster = example.GetSizePrefixedRootAsMonster(buf, offset)
	} else {
		monster = example.GetRootAsMonster(buf, offset)
	}

	monster.MutateHp(80)
	monster.MutateTestbool(true)
	monster.Pos(nil).MutateX(1.0)
	monster.Pos(nil).MutateY(2.0)
	monster.Pos(nil).MutateZ(3.0)
	monster.Pos(nil).MutateTest1(3.0)
	monster.Pos(nil).MutateTest2(example.ColorGreen)
	monster.Pos(nil).Test3(nil).MutateA(5)
	monster.Pos(nil).Test3(nil).MutateB(6)
	monster.MutateInventory(2, 2)

	for _, t := range testForOriginalValues {
		if !t.testfn() {
			fail("field '" + t.field + "' doesn't have the expected original value")
		}
	}

	// buffer should have original values
	if !reflect.DeepEqual(buf, org) {
		fail("revert changes failed")
	}
}

func CheckObjectAPI(buf []byte, offset flatbuffers.UOffsetT, sizePrefix bool, fail func(string, ...interface{})) {
	var monster *example.MonsterT

	if sizePrefix {
		monster = example.GetSizePrefixedRootAsMonster(buf, offset).UnPack()
	} else {
		monster = example.GetRootAsMonster(buf, offset).UnPack()
	}

	if got := monster.Hp; 80 != got {
		fail(FailString("hp", 80, got))
	}

	// default
	if got := monster.Mana; 150 != got {
		fail(FailString("mana", 150, got))
	}

	builder := flatbuffers.NewBuilder(0)
	builder.Finish(monster.Pack(builder))
	monster2 := example.GetRootAsMonster(builder.FinishedBytes(), 0).UnPack()
	if !reflect.DeepEqual(monster, monster2) {
		fail(FailString("Pack/Unpack()", monster, monster2))
	}
}

// Low level stress/fuzz test: serialize/deserialize a variety of
// different kinds of data in different combinations
func checkFuzz(fuzzFields, fuzzObjects int, fail func(string, ...interface{})) {

	// Values we're testing against: chosen to ensure no bits get chopped
	// off anywhere, and also be different from eachother.
	boolVal := true
	int8Val := int8(-127) // 0x81
	uint8Val := uint8(0xFF)
	int16Val := int16(-32222) // 0x8222
	uint16Val := uint16(0xFEEE)
	int32Val := int32(overflowingInt32Val)
	uint32Val := uint32(0xFDDDDDDD)
	int64Val := int64(overflowingInt64Val)
	uint64Val := uint64(0xFCCCCCCCCCCCCCCC)
	float32Val := float32(3.14159)
	float64Val := float64(3.14159265359)

	testValuesMax := 11 // hardcoded to the number of scalar types

	builder := flatbuffers.NewBuilder(0)
	l := NewLCG()

	objects := make([]flatbuffers.UOffsetT, fuzzObjects)

	// Generate fuzzObjects random objects each consisting of
	// fuzzFields fields, each of a random type.
	for i := 0; i < fuzzObjects; i++ {
		builder.StartObject(fuzzFields)

		for f := 0; f < fuzzFields; f++ {
			choice := l.Next() % uint32(testValuesMax)
			switch choice {
			case 0:
				builder.PrependBoolSlot(int(f), boolVal, false)
			case 1:
				builder.PrependInt8Slot(int(f), int8Val, 0)
			case 2:
				builder.PrependUint8Slot(int(f), uint8Val, 0)
			case 3:
				builder.PrependInt16Slot(int(f), int16Val, 0)
			case 4:
				builder.PrependUint16Slot(int(f), uint16Val, 0)
			case 5:
				builder.PrependInt32Slot(int(f), int32Val, 0)
			case 6:
				builder.PrependUint32Slot(int(f), uint32Val, 0)
			case 7:
				builder.PrependInt64Slot(int(f), int64Val, 0)
			case 8:
				builder.PrependUint64Slot(int(f), uint64Val, 0)
			case 9:
				builder.PrependFloat32Slot(int(f), float32Val, 0)
			case 10:
				builder.PrependFloat64Slot(int(f), float64Val, 0)
			}
		}

		off := builder.EndObject()

		// store the offset from the end of the builder buffer,
		// since it will keep growing:
		objects[i] = off
	}

	// Do some bookkeeping to generate stats on fuzzes:
	stats := map[string]int{}
	check := func(desc string, want, got interface{}) {
		stats[desc]++
		if want != got {
			fail("%s want %v got %v", desc, want, got)
		}
	}

	l = NewLCG() // Reset.

	// Test that all objects we generated are readable and return the
	// expected values. We generate random objects in the same order
	// so this is deterministic.
	for i := 0; i < fuzzObjects; i++ {

		table := &flatbuffers.Table{
			Bytes: builder.Bytes,
			Pos:   flatbuffers.UOffsetT(len(builder.Bytes)) - objects[i],
		}

		for j := 0; j < fuzzFields; j++ {
			f := flatbuffers.VOffsetT((flatbuffers.VtableMetadataFields + j) * flatbuffers.SizeVOffsetT)
			choice := l.Next() % uint32(testValuesMax)

			switch choice {
			case 0:
				check("bool", boolVal, table.GetBoolSlot(f, false))
			case 1:
				check("int8", int8Val, table.GetInt8Slot(f, 0))
			case 2:
				check("uint8", uint8Val, table.GetUint8Slot(f, 0))
			case 3:
				check("int16", int16Val, table.GetInt16Slot(f, 0))
			case 4:
				check("uint16", uint16Val, table.GetUint16Slot(f, 0))
			case 5:
				check("int32", int32Val, table.GetInt32Slot(f, 0))
			case 6:
				check("uint32", uint32Val, table.GetUint32Slot(f, 0))
			case 7:
				check("int64", int64Val, table.GetInt64Slot(f, 0))
			case 8:
				check("uint64", uint64Val, table.GetUint64Slot(f, 0))
			case 9:
				check("float32", float32Val, table.GetFloat32Slot(f, 0))
			case 10:
				check("float64", float64Val, table.GetFloat64Slot(f, 0))
			}
		}
	}

	// If enough checks were made, verify that all scalar types were used:
	if fuzzFields*fuzzObjects >= testValuesMax {
		if len(stats) != testValuesMax {
			fail("fuzzing failed to test all scalar types")
		}
	}

	// Print some counts, if needed:
	if testing.Verbose() {
		if fuzzFields == 0 || fuzzObjects == 0 {
			fmt.Printf("fuzz\tfields: %d\tobjects: %d\t[none]\t%d\n",
				fuzzFields, fuzzObjects, 0)
		} else {
			keys := make([]string, 0, len(stats))
			for k := range stats {
				keys = append(keys, k)
			}
			sort.Strings(keys)
			for _, k := range keys {
				fmt.Printf("fuzz\tfields: %d\tobjects: %d\t%s\t%d\n",
					fuzzFields, fuzzObjects, k, stats[k])
			}
		}
	}

	return
}

// FailString makes a message for when expectations differ from reality.
func FailString(name string, want, got interface{}) string {
	return fmt.Sprintf("bad %s: want %#v got %#v", name, want, got)
}

// CheckByteLayout verifies the bytes of a Builder in various scenarios.
func CheckByteLayout(fail func(string, ...interface{})) {
	var b *flatbuffers.Builder

	var i int
	check := func(want []byte) {
		i++
		got := b.Bytes[b.Head():]
		if !bytes.Equal(want, got) {
			fail("case %d: want\n%v\nbut got\n%v\n", i, want, got)
		}
	}

	// test 1: numbers

	b = flatbuffers.NewBuilder(0)
	check([]byte{})
	b.PrependBool(true)
	check([]byte{1})
	b.PrependInt8(-127)
	check([]byte{129, 1})
	b.PrependUint8(255)
	check([]byte{255, 129, 1})
	b.PrependInt16(-32222)
	check([]byte{0x22, 0x82, 0, 255, 129, 1}) // first pad
	b.PrependUint16(0xFEEE)
	check([]byte{0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1}) // no pad this time
	b.PrependInt32(-53687092)
	check([]byte{204, 204, 204, 252, 0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1})
	b.PrependUint32(0x98765432)
	check([]byte{0x32, 0x54, 0x76, 0x98, 204, 204, 204, 252, 0xEE, 0xFE, 0x22, 0x82, 0, 255, 129, 1})

	// test 1b: numbers 2

	b = flatbuffers.NewBuilder(0)
	b.PrependUint64(0x1122334455667788)
	check([]byte{0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11})

	// test 2: 1xbyte vector

	b = flatbuffers.NewBuilder(0)
	check([]byte{})
	b.StartVector(flatbuffers.SizeByte, 1, 1)
	check([]byte{0, 0, 0}) // align to 4bytes
	b.PrependByte(1)
	check([]byte{1, 0, 0, 0})
	b.EndVector(1)
	check([]byte{1, 0, 0, 0, 1, 0, 0, 0}) // padding

	// test 3: 2xbyte vector

	b = flatbuffers.NewBuilder(0)
	b.StartVector(flatbuffers.SizeByte, 2, 1)
	check([]byte{0, 0}) // align to 4bytes
	b.PrependByte(1)
	check([]byte{1, 0, 0})
	b.PrependByte(2)
	check([]byte{2, 1, 0, 0})
	b.EndVector(2)
	check([]byte{2, 0, 0, 0, 2, 1, 0, 0}) // padding

	// test 3b: 11xbyte vector matches builder size

	b = flatbuffers.NewBuilder(12)
	b.StartVector(flatbuffers.SizeByte, 8, 1)
	start := []byte{}
	check(start)
	for i := 1; i < 12; i++ {
		b.PrependByte(byte(i))
		start = append([]byte{byte(i)}, start...)
		check(start)
	}
	b.EndVector(8)
	check(append([]byte{8, 0, 0, 0}, start...))

	// test 4: 1xuint16 vector

	b = flatbuffers.NewBuilder(0)
	b.StartVector(flatbuffers.SizeUint16, 1, 1)
	check([]byte{0, 0}) // align to 4bytes
	b.PrependUint16(1)
	check([]byte{1, 0, 0, 0})
	b.EndVector(1)
	check([]byte{1, 0, 0, 0, 1, 0, 0, 0}) // padding

	// test 5: 2xuint16 vector

	b = flatbuffers.NewBuilder(0)
	b.StartVector(flatbuffers.SizeUint16, 2, 1)
	check([]byte{}) // align to 4bytes
	b.PrependUint16(0xABCD)
	check([]byte{0xCD, 0xAB})
	b.PrependUint16(0xDCBA)
	check([]byte{0xBA, 0xDC, 0xCD, 0xAB})
	b.EndVector(2)
	check([]byte{2, 0, 0, 0, 0xBA, 0xDC, 0xCD, 0xAB})

	// test 6: CreateString

	b = flatbuffers.NewBuilder(0)
	b.CreateString("foo")
	check([]byte{3, 0, 0, 0, 'f', 'o', 'o', 0}) // 0-terminated, no pad
	b.CreateString("moop")
	check([]byte{4, 0, 0, 0, 'm', 'o', 'o', 'p', 0, 0, 0, 0, // 0-terminated, 3-byte pad
		3, 0, 0, 0, 'f', 'o', 'o', 0})

	// test 6b: CreateString unicode

	b = flatbuffers.NewBuilder(0)
	// These characters are chinese from blog.golang.org/strings
	// We use escape codes here so that editors without unicode support
	// aren't bothered:
	uni_str := "\u65e5\u672c\u8a9e"
	b.CreateString(uni_str)
	check([]byte{9, 0, 0, 0, 230, 151, 165, 230, 156, 172, 232, 170, 158, 0, //  null-terminated, 2-byte pad
		0, 0})

	// test 6c: CreateByteString

	b = flatbuffers.NewBuilder(0)
	b.CreateByteString([]byte("foo"))
	check([]byte{3, 0, 0, 0, 'f', 'o', 'o', 0}) // 0-terminated, no pad
	b.CreateByteString([]byte("moop"))
	check([]byte{4, 0, 0, 0, 'm', 'o', 'o', 'p', 0, 0, 0, 0, // 0-terminated, 3-byte pad
		3, 0, 0, 0, 'f', 'o', 'o', 0})

	// test 7: empty vtable
	b = flatbuffers.NewBuilder(0)
	b.StartObject(0)
	check([]byte{})
	b.EndObject()
	check([]byte{4, 0, 4, 0, 4, 0, 0, 0})

	// test 8: vtable with one true bool
	b = flatbuffers.NewBuilder(0)
	check([]byte{})
	b.StartObject(1)
	check([]byte{})
	b.PrependBoolSlot(0, true, false)
	b.EndObject()
	check([]byte{
		6, 0, // vtable bytes
		8, 0, // length of object including vtable offset
		7, 0, // start of bool value
		6, 0, 0, 0, // offset for start of vtable (int32)
		0, 0, 0, // padded to 4 bytes
		1, // bool value
	})

	// test 9: vtable with one default bool
	b = flatbuffers.NewBuilder(0)
	check([]byte{})
	b.StartObject(1)
	check([]byte{})
	b.PrependBoolSlot(0, false, false)
	b.EndObject()
	check([]byte{
		4, 0, // vtable bytes
		4, 0, // end of object from here
		// entry 1 is zero and not stored.
		4, 0, 0, 0, // offset for start of vtable (int32)
	})

	// test 10: vtable with one int16
	b = flatbuffers.NewBuilder(0)
	b.StartObject(1)
	b.PrependInt16Slot(0, 0x789A, 0)
	b.EndObject()
	check([]byte{
		6, 0, // vtable bytes
		8, 0, // end of object from here
		6, 0, // offset to value
		6, 0, 0, 0, // offset for start of vtable (int32)
		0, 0, // padding to 4 bytes
		0x9A, 0x78,
	})

	// test 11: vtable with two int16
	b = flatbuffers.NewBuilder(0)
	b.StartObject(2)
	b.PrependInt16Slot(0, 0x3456, 0)
	b.PrependInt16Slot(1, 0x789A, 0)
	b.EndObject()
	check([]byte{
		8, 0, // vtable bytes
		8, 0, // end of object from here
		6, 0, // offset to value 0
		4, 0, // offset to value 1
		8, 0, 0, 0, // offset for start of vtable (int32)
		0x9A, 0x78, // value 1
		0x56, 0x34, // value 0
	})

	// test 12: vtable with int16 and bool
	b = flatbuffers.NewBuilder(0)
	b.StartObject(2)
	b.PrependInt16Slot(0, 0x3456, 0)
	b.PrependBoolSlot(1, true, false)
	b.EndObject()
	check([]byte{
		8, 0, // vtable bytes
		8, 0, // end of object from here
		6, 0, // offset to value 0
		5, 0, // offset to value 1
		8, 0, 0, 0, // offset for start of vtable (int32)
		0,          // padding
		1,          // value 1
		0x56, 0x34, // value 0
	})

	// test 12: vtable with empty vector
	b = flatbuffers.NewBuilder(0)
	b.StartVector(flatbuffers.SizeByte, 0, 1)
	vecend := b.EndVector(0)
	b.StartObject(1)
	b.PrependUOffsetTSlot(0, vecend, 0)
	b.EndObject()
	check([]byte{
		6, 0, // vtable bytes
		8, 0,
		4, 0, // offset to vector offset
		6, 0, 0, 0, // offset for start of vtable (int32)
		4, 0, 0, 0,
		0, 0, 0, 0, // length of vector (not in struct)
	})

	// test 12b: vtable with empty vector of byte and some scalars
	b = flatbuffers.NewBuilder(0)
	b.StartVector(flatbuffers.SizeByte, 0, 1)
	vecend = b.EndVector(0)
	b.StartObject(2)
	b.PrependInt16Slot(0, 55, 0)
	b.PrependUOffsetTSlot(1, vecend, 0)
	b.EndObject()
	check([]byte{
		8, 0, // vtable bytes
		12, 0,
		10, 0, // offset to value 0
		4, 0, // offset to vector offset
		8, 0, 0, 0, // vtable loc
		8, 0, 0, 0, // value 1
		0, 0, 55, 0, // value 0

		0, 0, 0, 0, // length of vector (not in struct)
	})

	// test 13: vtable with 1 int16 and 2-vector of int16
	b = flatbuffers.NewBuilder(0)
	b.StartVector(flatbuffers.SizeInt16, 2, 1)
	b.PrependInt16(0x1234)
	b.PrependInt16(0x5678)
	vecend = b.EndVector(2)
	b.StartObject(2)
	b.PrependUOffsetTSlot(1, vecend, 0)
	b.PrependInt16Slot(0, 55, 0)
	b.EndObject()
	check([]byte{
		8, 0, // vtable bytes
		12, 0, // length of object
		6, 0, // start of value 0 from end of vtable
		8, 0, // start of value 1 from end of buffer
		8, 0, 0, 0, // offset for start of vtable (int32)
		0, 0, // padding
		55, 0, // value 0
		4, 0, 0, 0, // vector position from here
		2, 0, 0, 0, // length of vector (uint32)
		0x78, 0x56, // vector value 1
		0x34, 0x12, // vector value 0
	})

	// test 14: vtable with 1 struct of 1 int8, 1 int16, 1 int32
	b = flatbuffers.NewBuilder(0)
	b.StartObject(1)
	b.Prep(4+4+4, 0)
	b.PrependInt8(55)
	b.Pad(3)
	b.PrependInt16(0x1234)
	b.Pad(2)
	b.PrependInt32(0x12345678)
	structStart := b.Offset()
	b.PrependStructSlot(0, structStart, 0)
	b.EndObject()
	check([]byte{
		6, 0, // vtable bytes
		16, 0, // end of object from here
		4, 0, // start of struct from here
		6, 0, 0, 0, // offset for start of vtable (int32)
		0x78, 0x56, 0x34, 0x12, // value 2
		0, 0, // padding
		0x34, 0x12, // value 1
		0, 0, 0, // padding
		55, // value 0
	})

	// test 15: vtable with 1 vector of 2 struct of 2 int8
	b = flatbuffers.NewBuilder(0)
	b.StartVector(flatbuffers.SizeInt8*2, 2, 1)
	b.PrependInt8(33)
	b.PrependInt8(44)
	b.PrependInt8(55)
	b.PrependInt8(66)
	vecend = b.EndVector(2)
	b.StartObject(1)
	b.PrependUOffsetTSlot(0, vecend, 0)
	b.EndObject()
	check([]byte{
		6, 0, // vtable bytes
		8, 0,
		4, 0, // offset of vector offset
		6, 0, 0, 0, // offset for start of vtable (int32)
		4, 0, 0, 0, // vector start offset

		2, 0, 0, 0, // vector length
		66, // vector value 1,1
		55, // vector value 1,0
		44, // vector value 0,1
		33, // vector value 0,0
	})

	// test 16: table with some elements
	b = flatbuffers.NewBuilder(0)
	b.StartObject(2)
	b.PrependInt8Slot(0, 33, 0)
	b.PrependInt16Slot(1, 66, 0)
	off := b.EndObject()
	b.Finish(off)

	check([]byte{
		12, 0, 0, 0, // root of table: points to vtable offset

		8, 0, // vtable bytes
		8, 0, // end of object from here
		7, 0, // start of value 0
		4, 0, // start of value 1

		8, 0, 0, 0, // offset for start of vtable (int32)

		66, 0, // value 1
		0,  // padding
		33, // value 0
	})

	// test 17: one unfinished table and one finished table
	b = flatbuffers.NewBuilder(0)
	b.StartObject(2)
	b.PrependInt8Slot(0, 33, 0)
	b.PrependInt8Slot(1, 44, 0)
	off = b.EndObject()
	b.Finish(off)

	b.StartObject(3)
	b.PrependInt8Slot(0, 55, 0)
	b.PrependInt8Slot(1, 66, 0)
	b.PrependInt8Slot(2, 77, 0)
	off = b.EndObject()
	b.Finish(off)

	check([]byte{
		16, 0, 0, 0, // root of table: points to object
		0, 0, // padding

		10, 0, // vtable bytes
		8, 0, // size of object
		7, 0, // start of value 0
		6, 0, // start of value 1
		5, 0, // start of value 2
		10, 0, 0, 0, // offset for start of vtable (int32)
		0,  // padding
		77, // value 2
		66, // value 1
		55, // value 0

		12, 0, 0, 0, // root of table: points to object

		8, 0, // vtable bytes
		8, 0, // size of object
		7, 0, // start of value 0
		6, 0, // start of value 1
		8, 0, 0, 0, // offset for start of vtable (int32)
		0, 0, // padding
		44, // value 1
		33, // value 0
	})

	// test 18: a bunch of bools
	b = flatbuffers.NewBuilder(0)
	b.StartObject(8)
	b.PrependBoolSlot(0, true, false)
	b.PrependBoolSlot(1, true, false)
	b.PrependBoolSlot(2, true, false)
	b.PrependBoolSlot(3, true, false)
	b.PrependBoolSlot(4, true, false)
	b.PrependBoolSlot(5, true, false)
	b.PrependBoolSlot(6, true, false)
	b.PrependBoolSlot(7, true, false)
	off = b.EndObject()
	b.Finish(off)

	check([]byte{
		24, 0, 0, 0, // root of table: points to vtable offset

		20, 0, // vtable bytes
		12, 0, // size of object
		11, 0, // start of value 0
		10, 0, // start of value 1
		9, 0, // start of value 2
		8, 0, // start of value 3
		7, 0, // start of value 4
		6, 0, // start of value 5
		5, 0, // start of value 6
		4, 0, // start of value 7
		20, 0, 0, 0, // vtable offset

		1, // value 7
		1, // value 6
		1, // value 5
		1, // value 4
		1, // value 3
		1, // value 2
		1, // value 1
		1, // value 0
	})

	// test 19: three bools
	b = flatbuffers.NewBuilder(0)
	b.StartObject(3)
	b.PrependBoolSlot(0, true, false)
	b.PrependBoolSlot(1, true, false)
	b.PrependBoolSlot(2, true, false)
	off = b.EndObject()
	b.Finish(off)

	check([]byte{
		16, 0, 0, 0, // root of table: points to vtable offset

		0, 0, // padding

		10, 0, // vtable bytes
		8, 0, // size of object
		7, 0, // start of value 0
		6, 0, // start of value 1
		5, 0, // start of value 2
		10, 0, 0, 0, // vtable offset from here

		0, // padding
		1, // value 2
		1, // value 1
		1, // value 0
	})

	// test 20: some floats
	b = flatbuffers.NewBuilder(0)
	b.StartObject(1)
	b.PrependFloat32Slot(0, 1.0, 0.0)
	off = b.EndObject()

	check([]byte{
		6, 0, // vtable bytes
		8, 0, // size of object
		4, 0, // start of value 0
		6, 0, 0, 0, // vtable offset

		0, 0, 128, 63, // value 0
	})
}

// CheckManualBuild builds a Monster manually.
func CheckManualBuild(fail func(string, ...interface{})) ([]byte, flatbuffers.UOffsetT) {
	b := flatbuffers.NewBuilder(0)
	str := b.CreateString("MyMonster")

	b.StartVector(1, 5, 1)
	b.PrependByte(4)
	b.PrependByte(3)
	b.PrependByte(2)
	b.PrependByte(1)
	b.PrependByte(0)
	inv := b.EndVector(5)

	b.StartObject(13)
	b.PrependInt16Slot(2, 20, 100)
	mon2 := b.EndObject()

	// Test4Vector
	b.StartVector(4, 2, 1)

	// Test 0
	b.Prep(2, 4)
	b.Pad(1)
	b.PlaceInt8(20)
	b.PlaceInt16(10)

	// Test 1
	b.Prep(2, 4)
	b.Pad(1)
	b.PlaceInt8(40)
	b.PlaceInt16(30)

	// end testvector
	test4 := b.EndVector(2)

	b.StartObject(13)

	// a vec3
	b.Prep(16, 32)
	b.Pad(2)
	b.Prep(2, 4)
	b.Pad(1)
	b.PlaceByte(6)
	b.PlaceInt16(5)
	b.Pad(1)
	b.PlaceByte(4)
	b.PlaceFloat64(3.0)
	b.Pad(4)
	b.PlaceFloat32(3.0)
	b.PlaceFloat32(2.0)
	b.PlaceFloat32(1.0)
	vec3Loc := b.Offset()
	// end vec3

	b.PrependStructSlot(0, vec3Loc, 0) // vec3. noop
	b.PrependInt16Slot(2, 80, 100)     // hp
	b.PrependUOffsetTSlot(3, str, 0)
	b.PrependUOffsetTSlot(5, inv, 0) // inventory
	b.PrependByteSlot(7, 1, 0)
	b.PrependUOffsetTSlot(8, mon2, 0)
	b.PrependUOffsetTSlot(9, test4, 0)
	mon := b.EndObject()

	b.Finish(mon)

	return b.Bytes, b.Head()
}

func CheckGetRootAsForNonRootTable(fail func(string, ...interface{})) {
	b := flatbuffers.NewBuilder(0)
	str := b.CreateString("MyStat")
	example.StatStart(b)
	example.StatAddId(b, str)
	example.StatAddVal(b, 12345678)
	example.StatAddCount(b, 12345)
	stat_end := example.StatEnd(b)
	b.Finish(stat_end)

	stat := example.GetRootAsStat(b.Bytes, b.Head())

	if got := stat.Id(); !bytes.Equal([]byte("MyStat"), got) {
		fail(FailString("stat.Id()", "MyStat", got))
	}

	if got := stat.Val(); 12345678 != got {
		fail(FailString("stat.Val()", 12345678, got))
	}

	if got := stat.Count(); 12345 != got {
		fail(FailString("stat.Count()", 12345, got))
	}
}

// CheckGeneratedBuild uses generated code to build the example Monster.
func CheckGeneratedBuild(sizePrefix bool, fail func(string, ...interface{})) ([]byte, flatbuffers.UOffsetT) {
	b := flatbuffers.NewBuilder(0)
	str := b.CreateString("MyMonster")
	test1 := b.CreateString("test1")
	test2 := b.CreateString("test2")
	fred := b.CreateString("Fred")

	example.MonsterStartInventoryVector(b, 5)
	b.PrependByte(4)
	b.PrependByte(3)
	b.PrependByte(2)
	b.PrependByte(1)
	b.PrependByte(0)
	inv := b.EndVector(5)

	example.MonsterStart(b)
	example.MonsterAddName(b, fred)
	mon2 := example.MonsterEnd(b)

	example.MonsterStartTest4Vector(b, 2)
	example.CreateTest(b, 10, 20)
	example.CreateTest(b, 30, 40)
	test4 := b.EndVector(2)

	example.MonsterStartTestarrayofstringVector(b, 2)
	b.PrependUOffsetT(test2)
	b.PrependUOffsetT(test1)
	testArrayOfString := b.EndVector(2)

	example.MonsterStart(b)

	pos := example.CreateVec3(b, 1.0, 2.0, 3.0, 3.0, example.ColorGreen, 5, 6)
	example.MonsterAddPos(b, pos)

	example.MonsterAddHp(b, 80)
	example.MonsterAddName(b, str)
	example.MonsterAddTestbool(b, true)
	example.MonsterAddInventory(b, inv)
	example.MonsterAddTestType(b, 1)
	example.MonsterAddTest(b, mon2)
	example.MonsterAddTest4(b, test4)
	example.MonsterAddTestarrayofstring(b, testArrayOfString)
	mon := example.MonsterEnd(b)

	if sizePrefix {
		b.FinishSizePrefixed(mon)
	} else {
		b.Finish(mon)
	}

	return b.Bytes, b.Head()
}

// CheckTableAccessors checks that the table accessors work as expected.
func CheckTableAccessors(fail func(string, ...interface{})) {
	// test struct accessor
	b := flatbuffers.NewBuilder(0)
	pos := example.CreateVec3(b, 1.0, 2.0, 3.0, 3.0, 4, 5, 6)
	b.Finish(pos)
	vec3Bytes := b.FinishedBytes()
	vec3 := &example.Vec3{}
	flatbuffers.GetRootAs(vec3Bytes, 0, vec3)

	if bytes.Compare(vec3Bytes, vec3.Table().Bytes) != 0 {
		fail("invalid vec3 table")
	}

	// test table accessor
	b = flatbuffers.NewBuilder(0)
	str := b.CreateString("MyStat")
	example.StatStart(b)
	example.StatAddId(b, str)
	example.StatAddVal(b, 12345678)
	example.StatAddCount(b, 12345)
	pos = example.StatEnd(b)
	b.Finish(pos)
	statBytes := b.FinishedBytes()
	stat := &example.Stat{}
	flatbuffers.GetRootAs(statBytes, 0, stat)

	if bytes.Compare(statBytes, stat.Table().Bytes) != 0 {
		fail("invalid stat table")
	}
}

// CheckVtableDeduplication verifies that vtables are deduplicated.
func CheckVtableDeduplication(fail func(string, ...interface{})) {
	b := flatbuffers.NewBuilder(0)

	b.StartObject(4)
	b.PrependByteSlot(0, 0, 0)
	b.PrependByteSlot(1, 11, 0)
	b.PrependByteSlot(2, 22, 0)
	b.PrependInt16Slot(3, 33, 0)
	obj0 := b.EndObject()

	b.StartObject(4)
	b.PrependByteSlot(0, 0, 0)
	b.PrependByteSlot(1, 44, 0)
	b.PrependByteSlot(2, 55, 0)
	b.PrependInt16Slot(3, 66, 0)
	obj1 := b.EndObject()

	b.StartObject(4)
	b.PrependByteSlot(0, 0, 0)
	b.PrependByteSlot(1, 77, 0)
	b.PrependByteSlot(2, 88, 0)
	b.PrependInt16Slot(3, 99, 0)
	obj2 := b.EndObject()

	got := b.Bytes[b.Head():]

	want := []byte{
		240, 255, 255, 255, // == -12. offset to dedupped vtable.
		99, 0,
		88,
		77,
		248, 255, 255, 255, // == -8. offset to dedupped vtable.
		66, 0,
		55,
		44,
		12, 0,
		8, 0,
		0, 0,
		7, 0,
		6, 0,
		4, 0,
		12, 0, 0, 0,
		33, 0,
		22,
		11,
	}

	if !bytes.Equal(want, got) {
		fail("testVtableDeduplication want:\n%d %v\nbut got:\n%d %v\n",
			len(want), want, len(got), got)
	}

	table0 := &flatbuffers.Table{Bytes: b.Bytes, Pos: flatbuffers.UOffsetT(len(b.Bytes)) - obj0}
	table1 := &flatbuffers.Table{Bytes: b.Bytes, Pos: flatbuffers.UOffsetT(len(b.Bytes)) - obj1}
	table2 := &flatbuffers.Table{Bytes: b.Bytes, Pos: flatbuffers.UOffsetT(len(b.Bytes)) - obj2}

	testTable := func(tab *flatbuffers.Table, a flatbuffers.VOffsetT, b, c, d byte) {
		// vtable size
		if got := tab.GetVOffsetTSlot(0, 0); 12 != got {
			fail("failed 0, 0: %d", got)
		}
		// object size
		if got := tab.GetVOffsetTSlot(2, 0); 8 != got {
			fail("failed 2, 0: %d", got)
		}
		// default value
		if got := tab.GetVOffsetTSlot(4, 0); a != got {
			fail("failed 4, 0: %d", got)
		}
		if got := tab.GetByteSlot(6, 0); b != got {
			fail("failed 6, 0: %d", got)
		}
		if val := tab.GetByteSlot(8, 0); c != val {
			fail("failed 8, 0: %d", got)
		}
		if got := tab.GetByteSlot(10, 0); d != got {
			fail("failed 10, 0: %d", got)
		}
	}

	testTable(table0, 0, 11, 22, 33)
	testTable(table1, 0, 44, 55, 66)
	testTable(table2, 0, 77, 88, 99)
}

// CheckNotInObjectError verifies that `EndObject` fails if not inside an
// object.
func CheckNotInObjectError(fail func(string, ...interface{})) {
	b := flatbuffers.NewBuilder(0)

	defer func() {
		r := recover()
		if r == nil {
			fail("expected panic in CheckNotInObjectError")
		}
	}()
	b.EndObject()
}

// CheckStringIsNestedError verifies that a string can not be created inside
// another object.
func CheckStringIsNestedError(fail func(string, ...interface{})) {
	b := flatbuffers.NewBuilder(0)
	b.StartObject(0)
	defer func() {
		r := recover()
		if r == nil {
			fail("expected panic in CheckStringIsNestedError")
		}
	}()
	b.CreateString("foo")
}

func CheckEmptiedBuilder(fail func(string, ...interface{})) {
	f := func(a, b string) bool {
		if a == b {
			return true
		}

		builder := flatbuffers.NewBuilder(0)

		a1 := builder.CreateSharedString(a)
		b1 := builder.CreateSharedString(b)
		builder.Reset()
		b2 := builder.CreateSharedString(b)
		a2 := builder.CreateSharedString(a)

		return !(a1 == a2 || b1 == b2)
	}
	if err := quick.Check(f, nil); err != nil {
		fail("expected different offset")
	}
}

func CheckSharedStrings(fail func(string, ...interface{})) {
	f := func(strings []string) bool {
		b := flatbuffers.NewBuilder(0)
		for _, s1 := range strings {
			for _, s2 := range strings {
				off1 := b.CreateSharedString(s1)
				off2 := b.CreateSharedString(s2)

				if (s1 == s2) && (off1 != off2) {
					return false
				}
				if (s1 != s2) && (off1 == off2) {
					return false
				}
			}
		}
		return true
	}
	if err := quick.Check(f, nil); err != nil {
		fail("expected same offset")
	}
}

// CheckByteStringIsNestedError verifies that a bytestring can not be created
// inside another object.
func CheckByteStringIsNestedError(fail func(string, ...interface{})) {
	b := flatbuffers.NewBuilder(0)
	b.StartObject(0)
	defer func() {
		r := recover()
		if r == nil {
			fail("expected panic in CheckByteStringIsNestedError")
		}
	}()
	b.CreateByteString([]byte("foo"))
}

// CheckStructIsNotInlineError verifies that writing a struct in a location
// away from where it is used will cause a panic.
func CheckStructIsNotInlineError(fail func(string, ...interface{})) {
	b := flatbuffers.NewBuilder(0)
	b.StartObject(0)
	defer func() {
		r := recover()
		if r == nil {
			fail("expected panic in CheckStructIsNotInlineError")
		}
	}()
	b.PrependStructSlot(0, 1, 0)
}

// CheckFinishedBytesError verifies that `FinishedBytes` panics if the table
// is not finished.
func CheckFinishedBytesError(fail func(string, ...interface{})) {
	b := flatbuffers.NewBuilder(0)

	defer func() {
		r := recover()
		if r == nil {
			fail("expected panic in CheckFinishedBytesError")
		}
	}()
	b.FinishedBytes()
}

// CheckEnumNames checks that the generated enum names are correct.
func CheckEnumNames(fail func(string, ...interface{})) {
	{
		want := map[example.Any]string{
			example.AnyNONE:                    "NONE",
			example.AnyMonster:                 "Monster",
			example.AnyTestSimpleTableWithEnum: "TestSimpleTableWithEnum",
			example.AnyMyGame_Example2_Monster: "MyGame_Example2_Monster",
		}
		got := example.EnumNamesAny
		if !reflect.DeepEqual(got, want) {
			fail("enum name is not equal")
		}
	}
	{
		want := map[example.Color]string{
			example.ColorRed:   "Red",
			example.ColorGreen: "Green",
			example.ColorBlue:  "Blue",
		}
		got := example.EnumNamesColor
		if !reflect.DeepEqual(got, want) {
			fail("enum name is not equal")
		}
	}
}

// CheckEnumString checks the String method on generated enum types.
func CheckEnumString(fail func(string, ...interface{})) {
	if got := example.AnyMonster.String(); got != "Monster" {
		fail("Monster.String: %q != %q", got, "Monster")
	}
	if got := fmt.Sprintf("color: %s", example.ColorGreen); got != "color: Green" {
		fail("color.String: %q != %q", got, "color: Green")
	}
}

// CheckEnumValues checks that the generated enum values maps are correct.
func CheckEnumValues(fail func(string, ...interface{})) {
	{
		want := map[string]example.Any{
			"NONE":                    example.AnyNONE,
			"Monster":                 example.AnyMonster,
			"TestSimpleTableWithEnum": example.AnyTestSimpleTableWithEnum,
			"MyGame_Example2_Monster": example.AnyMyGame_Example2_Monster,
		}
		got := example.EnumValuesAny
		if !reflect.DeepEqual(got, want) {
			fail("enum name is not equal")
		}
	}
	{
		want := map[string]example.Color{
			"Red":   example.ColorRed,
			"Green": example.ColorGreen,
			"Blue":  example.ColorBlue,
		}
		got := example.EnumValuesColor
		if !reflect.DeepEqual(got, want) {
			fail("enum name is not equal")
		}
	}
}

// CheckDocExample checks that the code given in FlatBuffers documentation
// is syntactically correct.
func CheckDocExample(buf []byte, off flatbuffers.UOffsetT, fail func(string, ...interface{})) {
	monster := example.GetRootAsMonster(buf, off)
	_ = monster.Hp()
	_ = monster.Pos(nil)
	for i := 0; i < monster.InventoryLength(); i++ {
		_ = monster.Inventory(i) // do something here
	}

	builder := flatbuffers.NewBuilder(0)

	example.MonsterStartInventoryVector(builder, 5)
	for i := 4; i >= 0; i-- {
		builder.PrependByte(byte(i))
	}
	inv := builder.EndVector(5)

	str := builder.CreateString("MyMonster")
	example.MonsterStart(builder)
	example.MonsterAddPos(builder, example.CreateVec3(builder, 1.0, 2.0, 3.0, 3.0, example.Color(4), 5, 6))
	example.MonsterAddHp(builder, 80)
	example.MonsterAddName(builder, str)
	example.MonsterAddInventory(builder, inv)
	example.MonsterAddTestType(builder, 1)
	example.MonsterAddColor(builder, example.ColorRed)
	// example.MonsterAddTest(builder, mon2)
	// example.MonsterAddTest4(builder, test4s)
	_ = example.MonsterEnd(builder)
}

func CheckCreateByteVector(fail func(string, ...interface{})) {
	raw := [30]byte{}
	for i := 0; i < len(raw); i++ {
		raw[i] = byte(i)
	}

	for size := 0; size < len(raw); size++ {
		b1 := flatbuffers.NewBuilder(0)
		b2 := flatbuffers.NewBuilder(0)
		b1.StartVector(1, size, 1)
		for i := size - 1; i >= 0; i-- {
			b1.PrependByte(raw[i])
		}
		b1.EndVector(size)
		b2.CreateByteVector(raw[:size])
		CheckByteEquality(b1.Bytes, b2.Bytes, fail)
	}
}

func CheckParentNamespace(fail func(string, ...interface{})) {
	var empty, nonempty []byte

	// create monster with an empty parent namespace field
	{
		builder := flatbuffers.NewBuilder(0)

		example.MonsterStart(builder)
		m := example.MonsterEnd(builder)
		builder.Finish(m)

		empty = make([]byte, len(builder.FinishedBytes()))
		copy(empty, builder.FinishedBytes())
	}

	// create monster with a non-empty parent namespace field
	{
		builder := flatbuffers.NewBuilder(0)
		mygame.InParentNamespaceStart(builder)
		pn := mygame.InParentNamespaceEnd(builder)

		example.MonsterStart(builder)
		example.MonsterAddParentNamespaceTest(builder, pn)
		m := example.MonsterEnd(builder)

		builder.Finish(m)

		nonempty = make([]byte, len(builder.FinishedBytes()))
		copy(nonempty, builder.FinishedBytes())
	}

	// read monster with empty parent namespace field
	{
		m := example.GetRootAsMonster(empty, 0)
		if m.ParentNamespaceTest(nil) != nil {
			fail("expected nil ParentNamespaceTest for empty field")
		}
	}

	// read monster with non-empty parent namespace field
	{
		m := example.GetRootAsMonster(nonempty, 0)
		if m.ParentNamespaceTest(nil) == nil {
			fail("expected non-nil ParentNamespaceTest for non-empty field")
		}
	}
}

func CheckSizePrefixedBuffer(fail func(string, ...interface{})) {
	// Generate a size-prefixed flatbuffer
	generated, off := CheckGeneratedBuild(true, fail)

	// Check that the size prefix is the size of monsterdata_go_wire.mon minus 4
	size := flatbuffers.GetSizePrefix(generated, off)
	if size != 220 {
		fail("mismatch between size prefix and expected size")
	}

	// Check that the buffer can be used as expected
	CheckReadBuffer(generated, off, true, fail)
	CheckMutateBuffer(generated, off, true, fail)
	CheckObjectAPI(generated, off, true, fail)

	// Write generated bfufer out to a file
	if err := os.WriteFile(outData+".sp", generated[off:], os.FileMode(0644)); err != nil {
		fail("failed to write file: %s", err)
	}
}

// Include simple random number generator to ensure results will be the
// same cross platform.
// http://en.wikipedia.org/wiki/Park%E2%80%93Miller_random_number_generator
type LCG uint32

const InitialLCGSeed = 48271

func NewLCG() *LCG {
	n := uint32(InitialLCGSeed)
	l := LCG(n)
	return &l
}

func (lcg *LCG) Reset() {
	*lcg = InitialLCGSeed
}

func (lcg *LCG) Next() uint32 {
	n := uint32((uint64(*lcg) * uint64(279470273)) % uint64(4294967291))
	*lcg = LCG(n)
	return n
}

// CheckByteEquality verifies that two byte buffers are the same.
func CheckByteEquality(a, b []byte, fail func(string, ...interface{})) {
	if !bytes.Equal(a, b) {
		fail("objects are not byte-wise equal")
	}
}

// CheckMutateMethods checks all mutate methods one by one
func CheckMutateMethods(fail func(string, ...interface{})) {
	b := flatbuffers.NewBuilder(0)
	b.StartObject(15)
	b.PrependBoolSlot(0, true, false)
	b.PrependByteSlot(1, 1, 0)
	b.PrependUint8Slot(2, 2, 0)
	b.PrependUint16Slot(3, 3, 0)
	b.PrependUint32Slot(4, 4, 0)
	b.PrependUint64Slot(5, 5, 0)
	b.PrependInt8Slot(6, 6, 0)
	b.PrependInt16Slot(7, 7, 0)
	b.PrependInt32Slot(8, 8, 0)
	b.PrependInt64Slot(9, 9, 0)
	b.PrependFloat32Slot(10, 10, 0)
	b.PrependFloat64Slot(11, 11, 0)

	b.PrependUOffsetTSlot(12, 12, 0)
	uoVal := b.Offset() - 12

	b.PrependVOffsetT(13)
	b.Slot(13)

	b.PrependSOffsetT(14)
	b.Slot(14)
	soVal := flatbuffers.SOffsetT(b.Offset() - 14)

	offset := b.EndObject()

	t := &flatbuffers.Table{
		Bytes: b.Bytes,
		Pos:   flatbuffers.UOffsetT(len(b.Bytes)) - offset,
	}

	calcVOffsetT := func(slot int) (vtableOffset flatbuffers.VOffsetT) {
		return flatbuffers.VOffsetT((flatbuffers.VtableMetadataFields + slot) * flatbuffers.SizeVOffsetT)
	}
	calcUOffsetT := func(vtableOffset flatbuffers.VOffsetT) (valueOffset flatbuffers.UOffsetT) {
		return t.Pos + flatbuffers.UOffsetT(t.Offset(vtableOffset))
	}

	type testcase struct {
		field  string
		testfn func() bool
	}

	testForOriginalValues := []testcase{
		testcase{"BoolSlot", func() bool { return t.GetBoolSlot(calcVOffsetT(0), true) == true }},
		testcase{"ByteSlot", func() bool { return t.GetByteSlot(calcVOffsetT(1), 1) == 1 }},
		testcase{"Uint8Slot", func() bool { return t.GetUint8Slot(calcVOffsetT(2), 2) == 2 }},
		testcase{"Uint16Slot", func() bool { return t.GetUint16Slot(calcVOffsetT(3), 3) == 3 }},
		testcase{"Uint32Slot", func() bool { return t.GetUint32Slot(calcVOffsetT(4), 4) == 4 }},
		testcase{"Uint64Slot", func() bool { return t.GetUint64Slot(calcVOffsetT(5), 5) == 5 }},
		testcase{"Int8Slot", func() bool { return t.GetInt8Slot(calcVOffsetT(6), 6) == 6 }},
		testcase{"Int16Slot", func() bool { return t.GetInt16Slot(calcVOffsetT(7), 7) == 7 }},
		testcase{"Int32Slot", func() bool { return t.GetInt32Slot(calcVOffsetT(8), 8) == 8 }},
		testcase{"Int64Slot", func() bool { return t.GetInt64Slot(calcVOffsetT(9), 9) == 9 }},
		testcase{"Float32Slot", func() bool { return t.GetFloat32Slot(calcVOffsetT(10), 10) == 10 }},
		testcase{"Float64Slot", func() bool { return t.GetFloat64Slot(calcVOffsetT(11), 11) == 11 }},
		testcase{"UOffsetTSlot", func() bool { return t.GetUOffsetT(calcUOffsetT(calcVOffsetT(12))) == uoVal }},
		testcase{"VOffsetTSlot", func() bool { return t.GetVOffsetT(calcUOffsetT(calcVOffsetT(13))) == 13 }},
		testcase{"SOffsetTSlot", func() bool { return t.GetSOffsetT(calcUOffsetT(calcVOffsetT(14))) == soVal }},
	}

	testMutability := []testcase{
		testcase{"BoolSlot", func() bool { return t.MutateBoolSlot(calcVOffsetT(0), false) }},
		testcase{"ByteSlot", func() bool { return t.MutateByteSlot(calcVOffsetT(1), 2) }},
		testcase{"Uint8Slot", func() bool { return t.MutateUint8Slot(calcVOffsetT(2), 4) }},
		testcase{"Uint16Slot", func() bool { return t.MutateUint16Slot(calcVOffsetT(3), 6) }},
		testcase{"Uint32Slot", func() bool { return t.MutateUint32Slot(calcVOffsetT(4), 8) }},
		testcase{"Uint64Slot", func() bool { return t.MutateUint64Slot(calcVOffsetT(5), 10) }},
		testcase{"Int8Slot", func() bool { return t.MutateInt8Slot(calcVOffsetT(6), 12) }},
		testcase{"Int16Slot", func() bool { return t.MutateInt16Slot(calcVOffsetT(7), 14) }},
		testcase{"Int32Slot", func() bool { return t.MutateInt32Slot(calcVOffsetT(8), 16) }},
		testcase{"Int64Slot", func() bool { return t.MutateInt64Slot(calcVOffsetT(9), 18) }},
		testcase{"Float32Slot", func() bool { return t.MutateFloat32Slot(calcVOffsetT(10), 20) }},
		testcase{"Float64Slot", func() bool { return t.MutateFloat64Slot(calcVOffsetT(11), 22) }},
		testcase{"UOffsetTSlot", func() bool { return t.MutateUOffsetT(calcUOffsetT(calcVOffsetT(12)), 24) }},
		testcase{"VOffsetTSlot", func() bool { return t.MutateVOffsetT(calcUOffsetT(calcVOffsetT(13)), 26) }},
		testcase{"SOffsetTSlot", func() bool { return t.MutateSOffsetT(calcUOffsetT(calcVOffsetT(14)), 28) }},
	}

	testMutabilityWithoutSlot := []testcase{
		testcase{"BoolSlot", func() bool { return t.MutateBoolSlot(calcVOffsetT(16), false) }},
		testcase{"ByteSlot", func() bool { return t.MutateByteSlot(calcVOffsetT(16), 2) }},
		testcase{"Uint8Slot", func() bool { return t.MutateUint8Slot(calcVOffsetT(16), 2) }},
		testcase{"Uint16Slot", func() bool { return t.MutateUint16Slot(calcVOffsetT(16), 2) }},
		testcase{"Uint32Slot", func() bool { return t.MutateUint32Slot(calcVOffsetT(16), 2) }},
		testcase{"Uint64Slot", func() bool { return t.MutateUint64Slot(calcVOffsetT(16), 2) }},
		testcase{"Int8Slot", func() bool { return t.MutateInt8Slot(calcVOffsetT(16), 2) }},
		testcase{"Int16Slot", func() bool { return t.MutateInt16Slot(calcVOffsetT(16), 2) }},
		testcase{"Int32Slot", func() bool { return t.MutateInt32Slot(calcVOffsetT(16), 2) }},
		testcase{"Int64Slot", func() bool { return t.MutateInt64Slot(calcVOffsetT(16), 2) }},
		testcase{"Float32Slot", func() bool { return t.MutateFloat32Slot(calcVOffsetT(16), 2) }},
		testcase{"Float64Slot", func() bool { return t.MutateFloat64Slot(calcVOffsetT(16), 2) }},
	}

	testForMutatedValues := []testcase{
		testcase{"BoolSlot", func() bool { return t.GetBoolSlot(calcVOffsetT(0), true) == false }},
		testcase{"ByteSlot", func() bool { return t.GetByteSlot(calcVOffsetT(1), 1) == 2 }},
		testcase{"Uint8Slot", func() bool { return t.GetUint8Slot(calcVOffsetT(2), 1) == 4 }},
		testcase{"Uint16Slot", func() bool { return t.GetUint16Slot(calcVOffsetT(3), 1) == 6 }},
		testcase{"Uint32Slot", func() bool { return t.GetUint32Slot(calcVOffsetT(4), 1) == 8 }},
		testcase{"Uint64Slot", func() bool { return t.GetUint64Slot(calcVOffsetT(5), 1) == 10 }},
		testcase{"Int8Slot", func() bool { return t.GetInt8Slot(calcVOffsetT(6), 1) == 12 }},
		testcase{"Int16Slot", func() bool { return t.GetInt16Slot(calcVOffsetT(7), 1) == 14 }},
		testcase{"Int32Slot", func() bool { return t.GetInt32Slot(calcVOffsetT(8), 1) == 16 }},
		testcase{"Int64Slot", func() bool { return t.GetInt64Slot(calcVOffsetT(9), 1) == 18 }},
		testcase{"Float32Slot", func() bool { return t.GetFloat32Slot(calcVOffsetT(10), 1) == 20 }},
		testcase{"Float64Slot", func() bool { return t.GetFloat64Slot(calcVOffsetT(11), 1) == 22 }},
		testcase{"UOffsetTSlot", func() bool { return t.GetUOffsetT(calcUOffsetT(calcVOffsetT(12))) == 24 }},
		testcase{"VOffsetTSlot", func() bool { return t.GetVOffsetT(calcUOffsetT(calcVOffsetT(13))) == 26 }},
		testcase{"SOffsetTSlot", func() bool { return t.GetSOffsetT(calcUOffsetT(calcVOffsetT(14))) == 28 }},
	}

	// make sure original values are okay
	for _, t := range testForOriginalValues {
		if !t.testfn() {
			fail(t.field + "' field doesn't have the expected original value")
		}
	}

	// try to mutate fields and check mutability
	for _, t := range testMutability {
		if !t.testfn() {
			fail(FailString(t.field+"' field failed mutability test", "passed", "failed"))
		}
	}

	// try to mutate fields and check mutability
	// these have wrong slots so should fail
	for _, t := range testMutabilityWithoutSlot {
		if t.testfn() {
			fail(FailString(t.field+"' field failed no slot mutability test", "failed", "passed"))
		}
	}

	// test whether values have changed
	for _, t := range testForMutatedValues {
		if !t.testfn() {
			fail(t.field + "' field doesn't have the expected mutated value")
		}
	}
}

// CheckOptionalScalars verifies against the ScalarStuff schema.
func CheckOptionalScalars(fail func(string, ...interface{})) {
	type testCase struct {
		what           string
		result, expect interface{}
	}

	makeDefaultTestCases := func(s *optional_scalars.ScalarStuff) []testCase {
		return []testCase{
			{"justI8", s.JustI8(), int8(0)},
			{"maybeI8", s.MaybeI8(), (*int8)(nil)},
			{"defaultI8", s.DefaultI8(), int8(42)},
			{"justU8", s.JustU8(), byte(0)},
			{"maybeU8", s.MaybeU8(), (*byte)(nil)},
			{"defaultU8", s.DefaultU8(), byte(42)},
			{"justI16", s.JustI16(), int16(0)},
			{"maybeI16", s.MaybeI16(), (*int16)(nil)},
			{"defaultI16", s.DefaultI16(), int16(42)},
			{"justU16", s.JustU16(), uint16(0)},
			{"maybeU16", s.MaybeU16(), (*uint16)(nil)},
			{"defaultU16", s.DefaultU16(), uint16(42)},
			{"justI32", s.JustI32(), int32(0)},
			{"maybeI32", s.MaybeI32(), (*int32)(nil)},
			{"defaultI32", s.DefaultI32(), int32(42)},
			{"justU32", s.JustU32(), uint32(0)},
			{"maybeU32", s.MaybeU32(), (*uint32)(nil)},
			{"defaultU32", s.DefaultU32(), uint32(42)},
			{"justI64", s.JustI64(), int64(0)},
			{"maybeI64", s.MaybeI64(), (*int64)(nil)},
			{"defaultI64", s.DefaultI64(), int64(42)},
			{"justU64", s.JustU64(), uint64(0)},
			{"maybeU64", s.MaybeU64(), (*uint64)(nil)},
			{"defaultU64", s.DefaultU64(), uint64(42)},
			{"justF32", s.JustF32(), float32(0)},
			{"maybeF32", s.MaybeF32(), (*float32)(nil)},
			{"defaultF32", s.DefaultF32(), float32(42)},
			{"justF64", s.JustF64(), float64(0)},
			{"maybeF64", s.MaybeF64(), (*float64)(nil)},
			{"defaultF64", s.DefaultF64(), float64(42)},
			{"justBool", s.JustBool(), false},
			{"maybeBool", s.MaybeBool(), (*bool)(nil)},
			{"defaultBool", s.DefaultBool(), true},
			{"justEnum", s.JustEnum(), optional_scalars.OptionalByte(0)},
			{"maybeEnum", s.MaybeEnum(), (*optional_scalars.OptionalByte)(nil)},
			{"defaultEnum", s.DefaultEnum(), optional_scalars.OptionalByteOne},
		}
	}

	makeAssignedTestCases := func(s *optional_scalars.ScalarStuff) []testCase {
		return []testCase{
			{"justI8", s.JustI8(), int8(5)},
			{"maybeI8", s.MaybeI8(), int8(5)},
			{"defaultI8", s.DefaultI8(), int8(5)},
			{"justU8", s.JustU8(), byte(6)},
			{"maybeU8", s.MaybeU8(), byte(6)},
			{"defaultU8", s.DefaultU8(), byte(6)},
			{"justI16", s.JustI16(), int16(7)},
			{"maybeI16", s.MaybeI16(), int16(7)},
			{"defaultI16", s.DefaultI16(), int16(7)},
			{"justU16", s.JustU16(), uint16(8)},
			{"maybeU16", s.MaybeU16(), uint16(8)},
			{"defaultU16", s.DefaultU16(), uint16(8)},
			{"justI32", s.JustI32(), int32(9)},
			{"maybeI32", s.MaybeI32(), int32(9)},
			{"defaultI32", s.DefaultI32(), int32(9)},
			{"justU32", s.JustU32(), uint32(10)},
			{"maybeU32", s.MaybeU32(), uint32(10)},
			{"defaultU32", s.DefaultU32(), uint32(10)},
			{"justI64", s.JustI64(), int64(11)},
			{"maybeI64", s.MaybeI64(), int64(11)},
			{"defaultI64", s.DefaultI64(), int64(11)},
			{"justU64", s.JustU64(), uint64(12)},
			{"maybeU64", s.MaybeU64(), uint64(12)},
			{"defaultU64", s.DefaultU64(), uint64(12)},
			{"justF32", s.JustF32(), float32(13)},
			{"maybeF32", s.MaybeF32(), float32(13)},
			{"defaultF32", s.DefaultF32(), float32(13)},
			{"justF64", s.JustF64(), float64(14)},
			{"maybeF64", s.MaybeF64(), float64(14)},
			{"defaultF64", s.DefaultF64(), float64(14)},
			{"justBool", s.JustBool(), true},
			{"maybeBool", s.MaybeBool(), true},
			{"defaultBool", s.DefaultBool(), false},
			{"justEnum", s.JustEnum(), optional_scalars.OptionalByteTwo},
			{"maybeEnum", s.MaybeEnum(), optional_scalars.OptionalByteTwo},
			{"defaultEnum", s.DefaultEnum(), optional_scalars.OptionalByteTwo},
		}
	}

	resolvePointer := func(v interface{}) interface{} {
		switch v := v.(type) {
		case *int8:
			return *v
		case *byte:
			return *v
		case *int16:
			return *v
		case *uint16:
			return *v
		case *int32:
			return *v
		case *uint32:
			return *v
		case *int64:
			return *v
		case *uint64:
			return *v
		case *float32:
			return *v
		case *float64:
			return *v
		case *bool:
			return *v
		case *optional_scalars.OptionalByte:
			return *v
		default:
			return v
		}
	}

	buildAssignedTable := func(b *flatbuffers.Builder) *optional_scalars.ScalarStuff {
		optional_scalars.ScalarStuffStart(b)
		optional_scalars.ScalarStuffAddJustI8(b, int8(5))
		optional_scalars.ScalarStuffAddMaybeI8(b, int8(5))
		optional_scalars.ScalarStuffAddDefaultI8(b, int8(5))
		optional_scalars.ScalarStuffAddJustU8(b, byte(6))
		optional_scalars.ScalarStuffAddMaybeU8(b, byte(6))
		optional_scalars.ScalarStuffAddDefaultU8(b, byte(6))
		optional_scalars.ScalarStuffAddJustI16(b, int16(7))
		optional_scalars.ScalarStuffAddMaybeI16(b, int16(7))
		optional_scalars.ScalarStuffAddDefaultI16(b, int16(7))
		optional_scalars.ScalarStuffAddJustU16(b, uint16(8))
		optional_scalars.ScalarStuffAddMaybeU16(b, uint16(8))
		optional_scalars.ScalarStuffAddDefaultU16(b, uint16(8))
		optional_scalars.ScalarStuffAddJustI32(b, int32(9))
		optional_scalars.ScalarStuffAddMaybeI32(b, int32(9))
		optional_scalars.ScalarStuffAddDefaultI32(b, int32(9))
		optional_scalars.ScalarStuffAddJustU32(b, uint32(10))
		optional_scalars.ScalarStuffAddMaybeU32(b, uint32(10))
		optional_scalars.ScalarStuffAddDefaultU32(b, uint32(10))
		optional_scalars.ScalarStuffAddJustI64(b, int64(11))
		optional_scalars.ScalarStuffAddMaybeI64(b, int64(11))
		optional_scalars.ScalarStuffAddDefaultI64(b, int64(11))
		optional_scalars.ScalarStuffAddJustU64(b, uint64(12))
		optional_scalars.ScalarStuffAddMaybeU64(b, uint64(12))
		optional_scalars.ScalarStuffAddDefaultU64(b, uint64(12))
		optional_scalars.ScalarStuffAddJustF32(b, float32(13))
		optional_scalars.ScalarStuffAddMaybeF32(b, float32(13))
		optional_scalars.ScalarStuffAddDefaultF32(b, float32(13))
		optional_scalars.ScalarStuffAddJustF64(b, float64(14))
		optional_scalars.ScalarStuffAddMaybeF64(b, float64(14))
		optional_scalars.ScalarStuffAddDefaultF64(b, float64(14))
		optional_scalars.ScalarStuffAddJustBool(b, true)
		optional_scalars.ScalarStuffAddMaybeBool(b, true)
		optional_scalars.ScalarStuffAddDefaultBool(b, false)
		optional_scalars.ScalarStuffAddJustEnum(b, optional_scalars.OptionalByteTwo)
		optional_scalars.ScalarStuffAddMaybeEnum(b, optional_scalars.OptionalByteTwo)
		optional_scalars.ScalarStuffAddDefaultEnum(b, optional_scalars.OptionalByteTwo)
		b.Finish(optional_scalars.ScalarStuffEnd(b))
		return optional_scalars.GetRootAsScalarStuff(b.FinishedBytes(), 0)
	}

	// test default values

	fbb := flatbuffers.NewBuilder(1)
	optional_scalars.ScalarStuffStart(fbb)
	fbb.Finish(optional_scalars.ScalarStuffEnd(fbb))
	ss := optional_scalars.GetRootAsScalarStuff(fbb.FinishedBytes(), 0)
	for _, tc := range makeDefaultTestCases(ss) {
		if tc.result != tc.expect {
			fail(FailString("Default ScalarStuff: "+tc.what, tc.expect, tc.result))
		}
	}

	// test assigned values
	fbb.Reset()
	ss = buildAssignedTable(fbb)
	for _, tc := range makeAssignedTestCases(ss) {
		if resolvePointer(tc.result) != tc.expect {
			fail(FailString("Assigned ScalarStuff: "+tc.what, tc.expect, tc.result))
		}
	}

	// test native object pack
	fbb.Reset()
	i8 := int8(5)
	u8 := byte(6)
	i16 := int16(7)
	u16 := uint16(8)
	i32 := int32(9)
	u32 := uint32(10)
	i64 := int64(11)
	u64 := uint64(12)
	f32 := float32(13)
	f64 := float64(14)
	b := true
	enum := optional_scalars.OptionalByteTwo
	obj := optional_scalars.ScalarStuffT{
		JustI8:      5,
		MaybeI8:     &i8,
		DefaultI8:   5,
		JustU8:      6,
		MaybeU8:     &u8,
		DefaultU8:   6,
		JustI16:     7,
		MaybeI16:    &i16,
		DefaultI16:  7,
		JustU16:     8,
		MaybeU16:    &u16,
		DefaultU16:  8,
		JustI32:     9,
		MaybeI32:    &i32,
		DefaultI32:  9,
		JustU32:     10,
		MaybeU32:    &u32,
		DefaultU32:  10,
		JustI64:     11,
		MaybeI64:    &i64,
		DefaultI64:  11,
		JustU64:     12,
		MaybeU64:    &u64,
		DefaultU64:  12,
		JustF32:     13,
		MaybeF32:    &f32,
		DefaultF32:  13,
		JustF64:     14,
		MaybeF64:    &f64,
		DefaultF64:  14,
		JustBool:    true,
		MaybeBool:   &b,
		DefaultBool: false,
		JustEnum:    optional_scalars.OptionalByteTwo,
		MaybeEnum:   &enum,
		DefaultEnum: optional_scalars.OptionalByteTwo,
	}
	fbb.Finish(obj.Pack(fbb))
	ss = optional_scalars.GetRootAsScalarStuff(fbb.FinishedBytes(), 0)
	for _, tc := range makeAssignedTestCases(ss) {
		if resolvePointer(tc.result) != tc.expect {
			fail(FailString("Native Object ScalarStuff: "+tc.what, tc.expect, tc.result))
		}
	}

	// test native object unpack
	fbb.Reset()
	ss = buildAssignedTable(fbb)
	ss.UnPackTo(&obj)
	expectEq := func(what string, a, b interface{}) {
		if resolvePointer(a) != b {
			fail(FailString("Native Object Unpack ScalarStuff: "+what, b, a))
		}
	}
	expectEq("justI8", obj.JustI8, int8(5))
	expectEq("maybeI8", obj.MaybeI8, int8(5))
	expectEq("defaultI8", obj.DefaultI8, int8(5))
	expectEq("justU8", obj.JustU8, byte(6))
	expectEq("maybeU8", obj.MaybeU8, byte(6))
	expectEq("defaultU8", obj.DefaultU8, byte(6))
	expectEq("justI16", obj.JustI16, int16(7))
	expectEq("maybeI16", obj.MaybeI16, int16(7))
	expectEq("defaultI16", obj.DefaultI16, int16(7))
	expectEq("justU16", obj.JustU16, uint16(8))
	expectEq("maybeU16", obj.MaybeU16, uint16(8))
	expectEq("defaultU16", obj.DefaultU16, uint16(8))
	expectEq("justI32", obj.JustI32, int32(9))
	expectEq("maybeI32", obj.MaybeI32, int32(9))
	expectEq("defaultI32", obj.DefaultI32, int32(9))
	expectEq("justU32", obj.JustU32, uint32(10))
	expectEq("maybeU32", obj.MaybeU32, uint32(10))
	expectEq("defaultU32", obj.DefaultU32, uint32(10))
	expectEq("justI64", obj.JustI64, int64(11))
	expectEq("maybeI64", obj.MaybeI64, int64(11))
	expectEq("defaultI64", obj.DefaultI64, int64(11))
	expectEq("justU64", obj.JustU64, uint64(12))
	expectEq("maybeU64", obj.MaybeU64, uint64(12))
	expectEq("defaultU64", obj.DefaultU64, uint64(12))
	expectEq("justF32", obj.JustF32, float32(13))
	expectEq("maybeF32", obj.MaybeF32, float32(13))
	expectEq("defaultF32", obj.DefaultF32, float32(13))
	expectEq("justF64", obj.JustF64, float64(14))
	expectEq("maybeF64", obj.MaybeF64, float64(14))
	expectEq("defaultF64", obj.DefaultF64, float64(14))
	expectEq("justBool", obj.JustBool, true)
	expectEq("maybeBool", obj.MaybeBool, true)
	expectEq("defaultBool", obj.DefaultBool, false)
	expectEq("justEnum", obj.JustEnum, optional_scalars.OptionalByteTwo)
	expectEq("maybeEnum", obj.MaybeEnum, optional_scalars.OptionalByteTwo)
	expectEq("defaultEnum", obj.DefaultEnum, optional_scalars.OptionalByteTwo)
}

// BenchmarkVtableDeduplication measures the speed of vtable deduplication
// by creating prePop vtables, then populating b.N objects with a
// different single vtable.
//
// When b.N is large (as in long benchmarks), memory usage may be high.
func BenchmarkVtableDeduplication(b *testing.B) {
	prePop := 10
	builder := flatbuffers.NewBuilder(0)

	// pre-populate some vtables:
	for i := 0; i < prePop; i++ {
		builder.StartObject(i)
		for j := 0; j < i; j++ {
			builder.PrependInt16Slot(j, int16(j), 0)
		}
		builder.EndObject()
	}

	// benchmark deduplication of a new vtable:
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		lim := prePop

		builder.StartObject(lim)
		for j := 0; j < lim; j++ {
			builder.PrependInt16Slot(j, int16(j), 0)
		}
		builder.EndObject()
	}
}

// BenchmarkParseGold measures the speed of parsing the 'gold' data
// used throughout this test suite.
func BenchmarkParseGold(b *testing.B) {
	buf, offset := CheckGeneratedBuild(false, b.Fatalf)
	monster := example.GetRootAsMonster(buf, offset)

	// use these to prevent allocations:
	reuse_pos := example.Vec3{}
	reuse_test3 := example.Test{}
	reuse_table2 := flatbuffers.Table{}
	reuse_monster2 := example.Monster{}
	reuse_test4_0 := example.Test{}
	reuse_test4_1 := example.Test{}

	b.SetBytes(int64(len(buf[offset:])))
	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		monster.Hp()
		monster.Mana()
		name := monster.Name()
		_ = name[0]
		_ = name[len(name)-1]

		monster.Pos(&reuse_pos)
		reuse_pos.X()
		reuse_pos.Y()
		reuse_pos.Z()
		reuse_pos.Test1()
		reuse_pos.Test2()
		reuse_pos.Test3(&reuse_test3)
		reuse_test3.A()
		reuse_test3.B()
		monster.TestType()
		monster.Test(&reuse_table2)
		reuse_monster2.Init(reuse_table2.Bytes, reuse_table2.Pos)
		name2 := reuse_monster2.Name()
		_ = name2[0]
		_ = name2[len(name2)-1]
		monster.InventoryLength()
		l := monster.InventoryLength()
		for i := 0; i < l; i++ {
			monster.Inventory(i)
		}
		monster.Test4Length()
		monster.Test4(&reuse_test4_0, 0)
		monster.Test4(&reuse_test4_1, 1)

		reuse_test4_0.A()
		reuse_test4_0.B()
		reuse_test4_1.A()
		reuse_test4_1.B()

		monster.TestarrayofstringLength()
		str0 := monster.Testarrayofstring(0)
		_ = str0[0]
		_ = str0[len(str0)-1]
		str1 := monster.Testarrayofstring(1)
		_ = str1[0]
		_ = str1[len(str1)-1]
	}
}

// BenchmarkBuildGold uses generated code to build the example Monster.
func BenchmarkBuildGold(b *testing.B) {
	buf, offset := CheckGeneratedBuild(false, b.Fatalf)
	bytes_length := int64(len(buf[offset:]))

	reuse_str := "MyMonster"
	reuse_test1 := "test1"
	reuse_test2 := "test2"
	reuse_fred := "Fred"

	b.SetBytes(bytes_length)
	bldr := flatbuffers.NewBuilder(0)
	b.ResetTimer()
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		bldr.Reset()

		str := bldr.CreateString(reuse_str)
		test1 := bldr.CreateString(reuse_test1)
		test2 := bldr.CreateString(reuse_test2)
		fred := bldr.CreateString(reuse_fred)

		example.MonsterStartInventoryVector(bldr, 5)
		bldr.PrependByte(4)
		bldr.PrependByte(3)
		bldr.PrependByte(2)
		bldr.PrependByte(1)
		bldr.PrependByte(0)
		inv := bldr.EndVector(5)

		example.MonsterStart(bldr)
		example.MonsterAddName(bldr, fred)
		mon2 := example.MonsterEnd(bldr)

		example.MonsterStartTest4Vector(bldr, 2)
		example.CreateTest(bldr, 10, 20)
		example.CreateTest(bldr, 30, 40)
		test4 := bldr.EndVector(2)

		example.MonsterStartTestarrayofstringVector(bldr, 2)
		bldr.PrependUOffsetT(test2)
		bldr.PrependUOffsetT(test1)
		testArrayOfString := bldr.EndVector(2)

		example.MonsterStart(bldr)

		pos := example.CreateVec3(bldr, 1.0, 2.0, 3.0, 3.0, example.ColorGreen, 5, 6)
		example.MonsterAddPos(bldr, pos)

		example.MonsterAddHp(bldr, 80)
		example.MonsterAddName(bldr, str)
		example.MonsterAddInventory(bldr, inv)
		example.MonsterAddTestType(bldr, 1)
		example.MonsterAddTest(bldr, mon2)
		example.MonsterAddTest4(bldr, test4)
		example.MonsterAddTestarrayofstring(bldr, testArrayOfString)
		mon := example.MonsterEnd(bldr)

		bldr.Finish(mon)
	}
}
